package com.gitee.chessboard.jdbc.executor.mybatis;

import com.gitee.chessboard.jdbc.JdbcManager;
import com.gitee.chessboard.jdbc.executor.AbstractSqlExecutor;
import com.gitee.chessboard.jdbc.ExecutorMapper;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * mybatis的Sql执行器
 *
 * @author 敲代码的旺财
 * @date 2021/11/18 14:37
 */
public class MybatisSqlExecutor extends AbstractSqlExecutor {

    private SqlSessionFactory sqlSessionFactory;

    private Map<Class<?>, Class<?>> clazzAndClazzMapper = new ConcurrentHashMap<>();

    public MybatisSqlExecutor(JdbcManager jdbcManager) {
        super(jdbcManager);
        this.sqlSessionFactory = jdbcManager.getMybatisSqlSessionFactory();
    }

    /**
     * 刷新SqlSessionFactory
     *
     * @param object 解析的原本对象
     */
    private synchronized void refreshSqlSessionFactory(Object object) {
        if (clazzAndClazzMapper.containsKey(object.getClass())) {
            return;
        }
        Configuration configuration = sqlSessionFactory.getConfiguration();
        configuration.getTypeAliasRegistry().registerAlias(object.getClass());
        //根据parser用字节码技术生成一个Mapper.class
        Class<?> mapper = MybatisMapperBuilder.createMapperClazz(getJdbcManager(), object);
        configuration.addMapper(mapper);
        clazzAndClazzMapper.put(object.getClass(), mapper);
    }


    /**
     * 执行创建表的sql
     *
     * @param object 解析的原本对象
     */
    @Override
    public void createTable(Object object) {
        if (!clazzAndClazzMapper.containsKey(object.getClass())) {
            refreshSqlSessionFactory(object);
        }
        SqlSession sqlSession = sqlSessionFactory.openSession();
        ExecutorMapper mapper = (ExecutorMapper) sqlSession.getMapper(clazzAndClazzMapper.get(object.getClass()));
        mapper.createTable();
        sqlSession.commit();
        sqlSession.close();
    }

    /**
     * 根据对象插入数据
     *
     * @param object 需要插入的对象
     */
    @Override
    public void insert(Object object) {
        if (!clazzAndClazzMapper.containsKey(object.getClass())) {
            refreshSqlSessionFactory(object);
        }
        SqlSession sqlSession = sqlSessionFactory.openSession();
        ExecutorMapper mapper = (ExecutorMapper) sqlSession.getMapper(clazzAndClazzMapper.get(object.getClass()));
        mapper.insert(object);
        sqlSession.commit();
        sqlSession.close();
    }

    /**
     * 根据对象插入数据或者更新数据
     *
     * @param object 需要插入的对象
     */
    @Override
    public void insertOrUpdate(Object object) {
        if (!clazzAndClazzMapper.containsKey(object.getClass())) {
            refreshSqlSessionFactory(object);
        }
        SqlSession sqlSession = sqlSessionFactory.openSession();
        ExecutorMapper mapper = (ExecutorMapper) sqlSession.getMapper(clazzAndClazzMapper.get(object.getClass()));
        mapper.insertOrUpdate(object);
        sqlSession.commit();
        sqlSession.close();
    }

    /**
     * 获取指定类型的所有对象
     * @return 指定类型的所有对象
     */
    @Override
    public <T> List<T> getAll(T object) {
        if (!clazzAndClazzMapper.containsKey(object.getClass())) {
            refreshSqlSessionFactory(object);
        }
        SqlSession sqlSession = sqlSessionFactory.openSession();
        ExecutorMapper mapper = (ExecutorMapper) sqlSession.getMapper(clazzAndClazzMapper.get(object.getClass()));
        List<T> result = mapper.getAll();
        sqlSession.commit();
        sqlSession.close();
        return result;
    }

    @Override
    public <T> int delete(T object) {
        if (!clazzAndClazzMapper.containsKey(object.getClass())) {
            refreshSqlSessionFactory(object);
        }
        SqlSession sqlSession = sqlSessionFactory.openSession();
        ExecutorMapper mapper = (ExecutorMapper) sqlSession.getMapper(clazzAndClazzMapper.get(object.getClass()));
        int result = mapper.delete(object);
        sqlSession.commit();
        sqlSession.close();
        return result;
    }
}
